function element_position(ele) {
    this.x = ele.offsetLeft;
    this.y = ele.offsetTop;
    this.ele = ele;

    while (this.ele.offsetParent != null) {
        this.ele = this.ele.offsetParent;
        this.x += this.ele.offsetLeft;
        this.y += this.ele.offsetTop;
    }
}

function add_event(ele, type, func) {
	if (ele.addEventListener) {
		ele.addEventListener(type, func, false);

	} if (ele.attachEvent) {
		ele.attachEvent('on' + type, func);
	}
}

function remove_event(ele, type, func) {
	if (ele.removeEventListener) {
		ele.removeEventListener(type, func, false);

	} if (ele.detachEvent) {
		ele.detachEvent('on' + type, func);
	}
}

function cancel_event(e) {
	var evt = e ? e : window.event;
	evt.cancelBubble = true;
}


var calendars = new Array();

function get_calendar(name) {
	for (var i = 0; i < calendars.length; i++) {
		if (calendars[i].name == name) {
			return calendars[i];
		}
	}
	return false;
}

function calendar_init(name, lang) {
	calendars[calendars.length] = new calendar(name, lang);
}

function calendar_parse_input(name) {
	get_calendar(name).parse_input();
}

function calendar_remove() {
	for (var i = 0; i < calendars.length; i++) {
		calendars[i].remove();
	}
}

function calendar_switcher(name, e) {
	get_calendar(name).calendar(e);
}

function calendar_draw(name, y, m, d) {
	get_calendar(name).draw(y, m, d);
}

function calendar_set(name, y, m, d) {
	get_calendar(name).set(y, m, d);
}

function calendar(name, lang) {
	this.name = name;
	this.lang = lang || 'en';

	switch (this.lang) {
		case 'ru':
			this.weekdays = new Array('П', 'В', 'С', 'Ч', 'П', 'С', 'В');
			this.months = new Array('Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь');
			this.months1 = new Array('Января', 'Февраля', 'Марта', 'Апреля', 'Мая', 'Июня', 'Июля', 'Августа', 'Сентября', 'Октября', 'Ноября', 'Декабря');
			break;
		
		case 'de':
			this.weekdays = new Array('M', 'D', 'M', 'D', 'F', 'S', 'S');
			this.months = new Array('Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember');
			this.months1 = new Array('Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember');
			break;
			
		default:
			this.weekdays = new Array('M', 'T', 'W', 'T', 'F', 'S', 'S');
			this.months = new Array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December');
			this.months1 = new Array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December');
			break;
	}

	this.get_ele_name = function() {
		return this.name + '_calendar_ele';
	}

	this.get_input_name = function() {
		return this.name + '_input';
	}

	this.get_ele = function() {
		return document.getElementById(this.get_ele_name());
	}

	this.get_date_ele = function() {
		return document.getElementById(this.name);
	}

	this.get_input_ele = function() {
		return document.getElementById(this.get_input_name());
	}

	this.string_to_date = function(string) {
		var date = stringToDate(string, this.lang);
		if (!date) {
			var now = new Date();
			date = new Date(now.getFullYear(), now.getMonth(), now.getDate());
		}

		return date;
	}

	this.get_sql_date = function(date) {
		var m = (date.getMonth() + 1).toString();
		if (m.length == 1) m = '0' + m;

		var d = date.getDate().toString();
		if (d.length == 1) d = '0' + d;

		return date.getFullYear() + '-' + m + '-' + d;		
	}

	this.get_date = function() {
		return this.string_to_date(this.get_input_ele().value);
	}

	this.set_date = function(y, m, d) {
		var date = new Date(y, m, d);
		this.get_input_ele().value = date.getDate() + ' ';
		if (this.lang == 'de') {
			this.get_input_ele().value = date.getDate() + '. '
				+ this.months1[date.getMonth()] + ' '
				+ date.getFullYear();
		} else if (this.lang == 'ru') {
			this.get_input_ele().value = date.getDate() + ' '
				+ this.months1[date.getMonth()].toLowerCase() + ' ' + date.getFullYear() + ' года';
		} else {
			this.get_input_ele().value = this.months1[date.getMonth()] + ' ' 
				+ date.getDate() + ', '
				+ date.getFullYear();
		}
		this.get_date_ele().value = this.get_sql_date(date);
	}

	this.clear_date = function() {
		this.get_input_ele().value = '';
		this.get_date_ele().value = '';
	}

	this.set = function(y, m, d) {
		this.set_date(y, m, d);
		this.remove();
	}

	this.calendar = function(e) {
		cancel_event(e);

		if (this.get_ele()) {
			this.remove();

		} else {
			calendar_remove();
			this.draw();
		}
	}

	this.remove = function() {
		if (this.get_ele()) {
			this.get_ele().parentNode.removeChild(this.get_ele());
			remove_event(document, 'click', calendar_remove);
		}
	}

	this.parse_input = function() {
		if (this.get_input_ele().value) {
			var date = this.get_date();
			
			var currentDate = new Date();
			var nextDate = new Date(date.getFullYear() + 1, date.getMonth(), date.getDate());
			
			var year = currentDate.getFullYear();
			if (daysBetween(currentDate, date) > daysBetween(currentDate, nextDate)) {
				year = nextDate.getFullYear();
			}
			
			this.set_date(year, date.getMonth(), date.getDate());
		} else {
			this.clear_date();
		}
	}

	this.init = function() {
		var name = this.name;
		var onblur = function() {
			calendar_parse_input(name);
		}
		add_event(this.get_input_ele(), 'blur', onblur);
		if (this.get_date_ele().value && this.get_date_ele().value != '0000-00-00') {
			var date = this.string_to_date(this.get_date_ele().value);
			this.set_date(date.getFullYear(), date.getMonth(), date.getDate());
		}
	}

	this.draw = function(y, m, d) {
		this.remove();

		var body_ele = document.getElementsByTagName('body')[0];
		var ele = document.createElement('div');
		var pos = new element_position(this.get_input_ele());

		ele.className = 'calendar';
		ele.id = this.get_ele_name();
		ele.style.left = pos.x + 'px';
		ele.style.top = pos.y + this.get_input_ele().offsetHeight + 5 + 'px';

		body_ele.appendChild(ele);
		add_event(ele, 'click', cancel_event);
		add_event(document, 'click', calendar_remove);

		var now = this.get_date();
		var calendar = y ? new Date(y, m, d) : this.get_date();
		var y = calendar.getFullYear();
		var m = calendar.getMonth();
		var d = calendar.getDate();
		var prev_m = m == 0 ? new Date(y - 1, 11, 1) : new Date(y, m - 1, 1);
		var next_m = m == 11 ? new Date(y + 1, 0, 1) : new Date(y, m + 1, 1);

		var content = '<table><thead>';
		content += '<tr class="year"><td><a class="calc_arr_l" onclick="calendar_draw(\'' + this.name + '\',' + (y - 1) + ',' + m + ',' + d + '); return false;"><img src="/img/left.gif" width="4" height="7" alt="" /></a></td>';
		content += '<td><a class="calc_arr_l" onclick="calendar_draw(\'' + this.name + '\',' + (y - 10) + ',' + m + ',' + d + '); return false;"><img src="/img/left.gif" width="4" height="7" alt="" /><img src="/img/left.gif" width="4" height="7" alt="" /></a></td>';
		content += '<td colspan="3">' + y + '</td>';
		content += '<td><a class="calc_arr_r" onclick="calendar_draw(\'' + this.name + '\',' + (y + 10) + ',' + m + ',' + d + '); return false;"><img src="/img/right.gif" width="4" height="7" alt="" /><img src="/img/right.gif" width="4" height="7" alt="" /></a></td>';
		content += '<td><a class="calc_arr_r" onclick="calendar_draw(\'' + this.name + '\',' + (y + 1) + ',' + m + ',' + d + '); return false;"><img src="/img/right.gif" width="4" height="7" alt="" /></a></td></tr>';
		content += '<tr class="month"><td><a class="calc_arr_l" onclick="calendar_draw(\'' + this.name + '\',' + prev_m.getFullYear() + ',' + prev_m.getMonth() + ',' + prev_m.getDate() + '); return false;"><img src="/img/left.gif" width="4" height="7" alt="" /></a></td>';
		content += '<td colspan="5">' + this.months[m] + '</td>';
		content += '<td><a class="calc_arr_r" onclick="calendar_draw(\'' + this.name + '\',' + next_m.getFullYear() + ',' + next_m.getMonth() + ',' + next_m.getDate() + '); return false;"><img src="/img/right.gif" width="4" height="7" alt="" /></a></td></tr>';

		content += '<tr class="weekdays">';
		for (var i = 0; i < this.weekdays.length; i++) {
			content += '<td' + (i > 4 ? ' class="weekend"' : '') + '>' + this.weekdays[i] + '</td>';
		}
		content += '</tr></thead><tbody><tr>';

		while (!(calendar.getDay() == 1 && (calendar.getDate() == 1 || calendar.getMonth() != m))) {
			calendar.setDate(calendar.getDate() - 1);
		}

		var class_name = '';
		while (!(calendar.getDay() == 1 && calendar.getMonth() == next_m.getMonth())) {
			if (calendar.getMonth() != m) class_name += 'notnow';
			if (calendar.getDay() == 0 || calendar.getDay() == 6) class_name += (class_name ? ' ' : '') + 'weekend';
			if (calendar.valueOf() == now.valueOf()) class_name += (class_name ? ' ' : '') + 'selected';

			content += '<td' + (class_name ? ' class="' + class_name + '"' : '') + '>';
			content += '<a onclick="calendar_set(\'' + this.name + '\',' + calendar.getFullYear() + ',' + calendar.getMonth() + ',' + calendar.getDate() + '); return false;">' + calendar.getDate() + '</a></td>';

			if (calendar.getDay() == 0) content += '</tr><tr>';
			calendar.setDate(calendar.getDate() + 1);
			class_name = '';
		}

		content += '</tr></tbody></table>';
		ele.innerHTML = content;
	}

	this.init();
}

function stringToDate(value, lang) {
	lang = lang || 'en';
	
	value = value.replace(/^\s+/, '')
		.replace(/\s+$/, '')
		.replace(/[\.,]/g, '')
		.toLowerCase();

	var now = new Date();
	var todayNoon = Date.UTC(
		now.getFullYear(), now.getMonth(), now.getDate(),
		12, 0, 0, 0
	);

	switch (value) {
		case 'vorgestern':
		case 'day before yesterday':
		case 'the day before yesterday':
		case 'позавчера':	return new Date(todayNoon - 1000 * 60 * 60 * 24 * 2);
		
		case 'yesterday':
		case 'gestern':
		case 'вчера':
			return new Date(todayNoon - 1000 * 60 * 60 * 24);
		
		case 'today':
		case 'heute':
		case 'сегодня':
			return new Date(todayNoon);
		
		case 'tomorrow':
		case 'morgen':
		case 'завтра':
			return new Date(todayNoon + 1000 * 60 * 60 * 24);
			
		case 'Übermorgen':
		case 'day after tomorrow':
		case 'the day after tomorrow':
		case 'послезавтра':	return new Date(todayNoon + 1000 * 60 * 60 * 24 * 2);
	}

	var match = /^([0-9]{1,2})[.\/\-]([0-9]{1,2})(?:[.\/\-]([0-9]{2,4}))?$/.exec(value);
	if (match) {
		var day = parseInt(match[1]);
		var month = parseInt(match[2]);
		var year = match[3] ? parseInt(match[3]) : now.getFullYear();

		if (30 >= year) year += 2000;
		else if (100 > year) year += 1900;

		if (isDate(year, month, day)) {
			return new Date(year, month - 1, day, 12, 0, 0);
		}
	}

	match = /^([0-9]{4})[.\/\-]([0-9]{1,2})[.\/\-]([0-9]{1,2})$/.exec(value);
	if (match) {
		var day = parseInt(match[3]);
		var month = parseInt(match[2]);
		var year = parseInt(match[1]);

		if (30 >= year) year += 2000;
		else if (100 > year) year += 1900;

		if (isDate(year, month, day)) {
			return new Date(year, month - 1, day, 12, 0, 0);
		}
	}

	var day = 0;
	var month = 0;
	var year = 0;

	match = /([0-9]{2,4}) ?(года|г\.|г)/.exec(value);
	if (match) {
		year = parseInt(match[1]);
		
		if (30 >= year) year += 2000;
		else if (100 > year) year += 1900;
		
		value = value.replace(/([0-9]{2,4}) ?(года|г\.|г)/, '');
		value = value.replace(/^\s+/, '').replace(/\s+$/, '');
	}

	switch (lang) {
		case 'de':
		case 'ru':
			var dayIndex = 0;
			var monthIndex = 1;
			break;
			
		default:
			var dayIndex = 1;
			var monthIndex = 0;
		
	}
	var date = value.split(' ');
	if (
		1 < date.length &&
		4 > date.length &&
		!(3 == date.length && 0 < year)
	) {
		day = parseInt(date[dayIndex]);
		month = guessMonth(date[monthIndex], lang);
		
		if (date[2]) year = parseInt(date[2]);
		else if (0 == year) year = now.getFullYear();
		
		if (isDate(year, month, day)) {
			return new Date(year, month - 1, day, 12, 0, 0);
		}
	}

	return false;
}

function guessMonth(value, lang) {
	lang = lang || 'en';
	value = value.toLowerCase();

	switch (lang) {
		case 'ru':
			var months = [
					['Январь', 'Января', 'Январе']
					, ['Февраль', 'Февраля', 'Феврале']
					, ['Март', 'Марта', 'Марте']
					, ['Апрель', 'Апреля', 'Апреле']
					, ['Май', 'Мая', 'Мае']
					, ['Июнь', 'Июня', 'Июне']
					, ['Июль', 'Июля', 'Июле']
					, ['Август', 'Августа', 'Августе']
					, ['Сентябрь', 'Сентября', 'Сентябре']
					, ['Октябрь', 'Октября', 'Октябре']
					, ['Ноябрь', 'Ноября', 'Ноябре']
					, ['Декабрь', 'Декабря', 'Декабре']
				];
			break;
			
		case 'de':
			var months = [
			  	['Januar']
				, ['Februar']
				, ['März']
				, ['April']
				, ['Mai']
				, ['Juni',]
				, ['Juli']
				, ['August']
				, ['September']
				, ['Oktober']
				, ['November']
				, ['Dezember']
			];
			break;
		
		default: // en
			var months = [
	  			['January']
				, ['February']
				, ['March']
				, ['April']
				, ['May']
				, ['June']
				, ['July']
				, ['August']
				, ['September']
				, ['October']
				, ['November']
	  			, ['December']
			];
			break;
	}

	var reg = new RegExp('^' + value);
	for (var i = 0; i < months.length; i++) {
		for (var j = 0; j < months[i].length; j++) {
			if (reg.test(months[i][j].toLowerCase())) {
				return i + 1;
			}
		}
	}

	return false;
}

function daysBetween(date1, date2) {

    // The number of milliseconds in one day
    var ONE_DAY = 1000 * 60 * 60 * 24

    // Convert both dates to milliseconds
    var date1_ms = date1.getTime()
    var date2_ms = date2.getTime()

    // Calculate the difference in milliseconds
    var difference_ms = Math.abs(date1_ms - date2_ms);
    
    // Convert back to days and return
    return Math.round(difference_ms/ONE_DAY);

}

function isDate(year, month, day) {
	return 0 < day && 32 > day && 0 < month && 13 > month && 1970 < year && 2030 > year;
}
